home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
modelers
/
geomview
/
source.lha
/
Geomview
/
src
/
bin
/
geomutil
/
plutil
/
polymerge.c
< prev
Wrap
C/C++ Source or Header
|
1993-07-28
|
17KB
|
788 lines
/*
* Squeeze OFF files.
* Merges collinear edges and coplanar faces.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h> /* for qsort(), malloc() */
extern char *strdup(const char *);
#define VSUB(a, b, dst) (dst)->x = (a)->x - (b)->x, \
(dst)->y = (a)->y - (b)->y, \
(dst)->z = (a)->z - (b)->z
#define VDOT(a, b) (a)->x*(b)->x + (a)->y*(b)->y + (a)->z*(b)->z
#define VCROSS(a, b, dst) (dst)->x = (a)->y * (b)->z - (a)->z * (b)->y, \
(dst)->y = (a)->z * (b)->x - (a)->x * (b)->z, \
(dst)->z = (a)->x * (b)->y - (a)->y * (b)->x
float EPS_LINEAR= 0.0001; /* Max unit-vector cross product for collinearity */
float EPS_NORMAL= 0.0009; /* Max sin^2 theta between normals for coplanarity */
float EPS_POINT= 0.00001;/* Max distance between coincident vertices */
#define F_VERT 0x1
#define F_EDGE 0x2
#define F_FACE 0x4
#define F_EVOLVER 0x8
typedef struct vertex V;
typedef struct face F;
typedef struct faceedge Fe;
typedef struct edge E;
typedef struct point {
float x, y, z, w;
} P;
struct vertex {
P p;
int ref;
int index;
float *more;
};
struct faceedge {
F *face;
Fe *prev, *next; /* double links in face-edges of this face */
E *edge;
int sign; /* direction w.r.t. edge */
P n; /* adopted surface normal at this edge */
Fe *elink;
};
struct face {
Fe *fedges;
char *affix;
};
struct edge {
V *v[2]; /* vertex pointers */
E *link; /* hash table link */
P to; /* normalized edge direction vector */
Fe *feds; /* faceedge's on this edge */
int index; /* for numbering edges for .fe format */
};
int nv, nf, tossedf, nhash;
V *Vs; /* All vertices */
F *Fs; /* All faces */
E **ehash; /* Array of hash-table head pointers */
int nhash;
int flags = F_VERT|F_EDGE|F_FACE;
int debug = 0;
int vdim = 3;
Fe *fedge( F *f, V *v0, V *v1 );
#define New(t) (t *) malloc(sizeof(t))
#define NewN(t, N) (t *) malloc((N)*sizeof(t))
main(argc, argv)
char *argv[];
{
register int i;
int tnv, tne, any;
int binary = 0, vertsize = 0;
char *C = "", *N = "", *four = "";
extern int optind;
extern char *optarg;
while((i = getopt(argc, argv, "bdv:e:f:VEF")) != EOF) {
switch(i) {
case 'b': flags |= F_EVOLVER; break;
case 'd': debug = 1; break;
case 'v': EPS_POINT = atof(optarg); break;
case 'e': EPS_LINEAR = atof(optarg); EPS_LINEAR *= EPS_LINEAR; break;
case 'f': EPS_NORMAL = atof(optarg); EPS_NORMAL *= EPS_NORMAL; break;
case 'V': flags &= ~F_VERT; break;
case 'E': flags &= ~F_EDGE; break;
case 'F': flags &= ~F_FACE; break;
default:
fprintf(stderr, "\
Usage: polymerge [-v vertex_thresh] [-e edge_thresh] [-f face_thresh]\n\
[-V][-E][-F][-d][-b] [infile.off]\n\
Merges coincident vertices, collinear edges, coplanar faces of an OFF object.\n\
-v vertex_thresh: max separation for \"coincident\" vertices (default %g)\n\
-e edge_thresh : max sin(theta) for \"collinear\" edges (default %g)\n\
-f face_thresh : max sin(theta) for \"coplanar\" facet normals (default %.4g)\n\
-V -E -F : don't try to merge vertices/edges/faces\n\
-b : create output in evolver .fe format\n\
-d : debug\n",
EPS_POINT, sqrt(EPS_LINEAR), sqrt(EPS_NORMAL));
exit(1);
}
}
if(optind < argc) {
if(freopen(argv[argc-1], "r", stdin) == NULL) {
fprintf(stderr, "polymerge: can't open input: ");
perror(argv[argc-1]);
exit(1);
}
}
vdim = 3;
for(;;) {
switch( fnextc(stdin, 0) ) {
case 'N': N = "N"; vertsize += 3; goto swallow;
case 'C': C = "C"; vertsize += 4; goto swallow;
case '4':
four = "4";
vdim = 4;
flags &= ~(F_EDGE|F_FACE);
goto swallow;
swallow:
case '{': case '=': fgetc(stdin); break;
case 'a': /* Appearance? Silently swallow keyword */
fexpectstr(stdin, "appearance");
if(fnextc(stdin, 0) == '{') {
int c, brack = 0;
do {
if((c = fgetc(stdin)) == '{') brack++;
else if(c == '}') brack--;
} while(brack > 0 && c != EOF);
}
break;
default: goto done;
}
}
done:
fexpecttoken(stdin, "OFF"); /* swallow optional OFF prefix */
if(fexpecttoken(stdin, "BINARY") == 0) {
binary = 1;
fnextc(stdin, 1);
fgetc(stdin);
}
if(fgetni(stdin, 1, &nv, 0) <= 0 ||
fgetni(stdin, 1, &nf, 0) <= 0 ||
fgetni(stdin, 1, &nhash, 0) <= 0 || nv <= 0 || nf <= 0) {
fprintf(stderr, "polymerge: Input not an OFF file.\n");
exit(1);
}
nhash = (nf + nv + 4) / 2;
if(nhash < 16) nhash = 17;
if(!(nhash & 1)) nhash++;
ehash = NewN(E *, nhash);
bzero((char *)ehash, nhash*sizeof(E *));
Vs = NewN(V, nv);
Fs = NewN(F, nf);
for(i = 0; i < nv; i++) {
V *vp = &Vs[i];
vp->p.w = 1;
if(fgetnf(stdin, vdim, &vp->p, binary) < vdim) {
badvert:
fprintf(stderr, "polymerge: error reading vertex %d/%d\n", i,nv);
exit(1);
}
if(vertsize) {
Vs[i].more = NewN(float, vertsize);
if(fgetnf(stdin, vertsize, Vs[i].more, binary) < vertsize)
goto badvert;
}
Vs[i].ref = 0;
Vs[i].index = i;
}
/*
* Combine vertices
*/
if(flags & F_VERT)
vmerge();
/*
* Load faces
* The fedge() calls here assume we've already merged all appropriate
* vertices.
*/
tossedf = 0;
for(i = 0; i < nf; i++) {
register F *fp = &Fs[i];
register Fe *fe;
int nfv, k, c;
int v, ov, v0;
Fe head;
char *cp;
char aff[512];
if(fgetni(stdin, 1, &nfv, binary) <= 0 || nfv <= 0) {
fprintf(stderr, "polymerge: error reading face %d/%d\n",
i, nf);
exit(1);
}
head.prev = head.next = &head;
fp->fedges = &head;
fgetni(stdin, 1, &v, binary);
if(v < 0 || v >= nv) {
fprintf(stderr, "polymerge: bad vertex %d on face %d\n", v, i);
exit(1);
}
v0 = ov = Vs[v].index; /* Use common vertex if merged */
for(k = 1; k < nfv; k++, ov = v) {
fgetni(stdin, 1, &v, binary);
if(v < 0 || v >= nv) {
fprintf(stderr, "polymerge: bad vertex %d on face %d\n", v, i);
exit(1);
}
v = Vs[v].index; /* Use common vertex if merged */
if(ov == v)
continue;
head.prev->next = fe = fedge(fp, &Vs[ov], &Vs[v]);
fe->prev = head.prev;
fe->next = &head;
head.prev = fe;
}
if(v != v0) {
fe = fedge(fp, &Vs[v], &Vs[v0]);
head.prev->next = fe;
fe->prev = head.prev;
fe->next = &head;
head.prev = fe;
}
head.next->prev = head.prev;
if(head.next == &head) {
/*
* Degenerate face here
*/
fp->fedges = NULL;
tossedf++;
debug && printf("# Face %d degenerate already\n", i);
} else {
head.prev->next = fp->fedges = head.next;
}
if(binary) {
int nfc;
float c;
fgetni(stdin, 1, &nfc, binary);
if(nfc > 4) {
fprintf(stderr, "Bad face color count 0x%x", nfc);
exit(1);
}
for(cp = aff; --nfc >= 0; cp += strlen(cp)) {
fgetnf(stdin, 1, &c, binary);
sprintf(cp, " %g", c);
}
} else {
(void) fnextc(stdin, 1);
for(cp = aff; (c = getc(stdin)) != EOF && c != '\n' && cp < &aff[511]; )
*cp++ = c;
}
*cp = '\0';
fp->affix = (cp > aff) ? strdup(aff) : "";
}
/*
* Compute face normals -- actually corner normals, since we avoid
* assuming faces are planar. As a side effect, we detect & join
* collinear edges.
*/
if(flags & F_EDGE) {
for(i = 0; i < nf; i++) {
normalize(&Fs[i]);
}
}
/*
* Locate edges bounding faces with the same normal direction.
*/
if(flags & F_FACE) {
do {
any = 0;
for(i = 0; i < nhash; i++) {
E *e;
for(e = ehash[i]; e != NULL; e = e->link) {
register Fe *fe, *fee;
for(fe = e->feds; fe != NULL; fe = fe->elink) {
for(fee = fe->elink; fee != NULL; fee = fee->elink) {
register float dn;
dn = VDOT(&fe->n, &fee->n);
if(fabs(1 - dn*dn) < EPS_NORMAL) {
/*
* OK, merge fee into fe
*/
femerge(fe, fee);
any++;
goto another;
}
}
}
another: ;
}
}
debug && printf("# %d faces merged this pass.\n", any);
} while(any);
}
/*
* Scan for unused edges.
*/
for(i = 0; i < nv; i++)
Vs[i].ref = 0;
tne = 0;
for(i = 0; i < nhash; i++) {
E *e;
for(e = ehash[i]; e != NULL; e = e->link) {
if(e->feds != NULL) {
e->v[0]->ref++;
e->v[1]->ref++;
e->index = ++tne;
}
}
}
/*
* Renumber used vertices.
*/
if(flags & 1) {
tnv = 0;
for(i = 0; i < nv; i++)
Vs[i].index = Vs[i].ref ? tnv++ : -i-1;
} else {
tnv = nv;
}
if (flags & F_EVOLVER) /* Produce Brakke's evolver .fe format */
{
int j=0;
if (vdim == 4) printf("space_dimension 4\n");
printf("vertices\n");
for(i = 0; i < nv; i++) {
register V *v = &Vs[i];
int k;
if(v->ref || !(flags & 1)) {
v->index = ++j;
printf("%d\t%g %g %g", v->index, v->p.x, v->p.y, v->p.z);
if(vdim == 4) printf(" %g", v->p.w);
if(vertsize) {
printf(" ");
for(k = 0; k < vertsize; k++)
printf(" %g", v->more[k]);
}
printf("\n");
}
}
printf("\nedges\n");
for(i = 0; i < nhash; i++) {
E *e;
for(e = ehash[i]; e != NULL; e = e->link)
if(e->feds != NULL)
printf("%d\t%d %d\n", e->index,e->v[0]->index,e->v[1]->index);
}
printf("\nfaces\n");
j=0;
for(i=0; i<nf; i++) {
register Fe *fe, *fee;
int k, nfv;
fe = Fs[i].fedges;
if(fe == NULL)
continue;
for(fee = fe, k = 1; (fee = fee->next) != fe; k++)
;
if ((nfv = k)<3)
continue; /* don't print faces of less than 3 sides */
printf("%d", ++j);
for(fee = fe, k = nfv; --k >= 0; fee = fee->next)
printf(" %d", (1-2*fee->sign)*fee->edge->index);
printf("\n");
}
}
else /* Produce OFF format */
{
printf("%s%s%sOFF\n%d %d %d\n", C, N, four, tnv, nf - tossedf, tne);
for(i = 0; i < nv; i++) {
register V *v = &Vs[i];
int k;
if(v->ref || !(flags & F_VERT)) {
printf("%g %g %g", v->p.x, v->p.y, v->p.z);
if(vdim == 4) printf(" %g", v->p.w);
if(vertsize) {
printf(" ");
for(k = 0; k < vertsize; k++)
printf(" %g", v->more[k]);
}
if(debug)
printf("\t# %d [%d] #%d", v->index, v->ref, i);
printf("\n");
}
}
printf("\n");
/* ho hum */
for(i=0; i<nf; i++) {
register Fe *fe, *fee;
int k, nfv;
fe = Fs[i].fedges;
if(fe == NULL)
continue;
for(fee = fe, k = 1; (fee = fee->next) != fe; k++)
;
nfv = k;
printf("%d", nfv);
for(fee = fe, k = nfv; --k >= 0; fee = fee->next)
printf(" %d", fee->edge->v[fee->sign]->index);
printf("\t%s", Fs[i].affix);
if(debug) {
printf(" #");
for(fee = fe, k = nfv; --k >= 0; fee = fee->next)
printf(" %d", fee->edge->v[fee->sign] - Vs);
}
printf("\n");
}
}
exit(1);
}
/*
* Add a new faceedge
*/
Fe *
fedge(F *f, V *v0, V *v1)
{
Fe *fe;
E *e;
int t;
int i0, i1;
float r;
fe = New(Fe);
fe->face = f;
fe->sign = 0;
i0 = v0->index;
i1 = v1->index;
if(i0 > i1) {
V *tv = v0;
v0 = v1;
v1 = tv;
i0 = v0->index;
i1 = v1->index;
fe->sign = 1;
}
t = (unsigned long)(v0->index + v1->index + v0->index*v1->index) % nhash;
/* Symmetric hash function */
for(e = ehash[t]; e != NULL; e = e->link)
if(e->v[0]->index == i0 && e->v[1]->index == i1)
goto gotit;
e = New(E);
e->v[0] = v0;
e->v[1] = v1;
e->feds = NULL;
VSUB(&v0->p, &v1->p, &e->to);
r = VDOT(&e->to, &e->to);
if(r != 0) {
r = 1/sqrt(r);
e->to.x *= r; e->to.y *= r; e->to.z *= r;
} else
debug && printf("# Coincident: %d == %d [%g %g %g]\n", v0->index, v1->index, v0->p.x,v0->p.y,v0->p.z);
e->link = ehash[t];
ehash[t] = e;
gotit:
fe->edge = e;
fe->elink = e->feds;
e->feds = fe;
return fe;
}
/*
* Remove a faceedge from its edge list
*/
unfedge(fe)
register Fe *fe;
{
register Fe **fepp;
for(fepp = &fe->edge->feds; *fepp != NULL; fepp = &(*fepp)->elink) {
if(*fepp == fe) {
*fepp = fe->elink;
break;
}
}
free(fe);
}
/*
* Merge two faces
* We delete these face-edges from both faces
*/
femerge(fe1, fe2)
register Fe *fe1, *fe2;
{
F *f1, *f2;
register Fe *tfe;
if(fe1->face == fe2->face) {
debug && printf("# Merging two edges of face %d -- tossing it.\n",
fe1->face - Fs);
deface(fe1->face);
return;
}
if(fe1->sign == fe2->sign) {
/*
* Messy. To merge these, we need to reverse all the links
* in one of the two faces.
*/
Fe *xfe;
tfe = fe2;
do {
tfe->sign ^= 1;
xfe = tfe->next;
tfe->next = tfe->prev;
tfe->prev = xfe;
tfe = xfe;
} while(tfe != fe2);
}
fe1->prev->next = fe2->next;
fe2->next->prev = fe1->prev;
fe1->next->prev = fe2->prev;
fe2->prev->next = fe1->next;
f1 = fe1->face;
f2 = fe2->face;
if(f1->fedges == fe1)
f1->fedges = fe2->next;
if(debug)
printf("# Merged face %d into %d (vertices %d %d) n1.n2 %g\n",
f2-Fs, f1-Fs,
fe1->edge->v[fe1->sign]->index,
fe1->edge->v[1 - fe1->sign]->index,
VDOT(&fe1->n, &fe2->n));
tfe = fe2->next;
if(tfe == NULL) {
fprintf(stderr, "polymerge: face f2 already deleted?\n");
} else {
do {
tfe->face = f1;
} while((tfe = tfe->next) != fe1->next);
}
f2->fedges = NULL;
tossedf++;
unfedge(fe1);
unfedge(fe2);
/*
* Join collinear edges, recompute normals (might have changed a bit).
*/
normalize(f1);
}
#define PRETTY(x) ((int)(x) - 0x10000000)
fecheck(fe)
Fe *fe;
{
register Fe *fee;
int ne;
int onface = 0;
register F *f;
register E *e;
if(fe == NULL)
return;
f = fe->face;
fprintf(stderr,"0x%x: on face %d (%x); ", fe, f - Fs, f);
fee = fe;
do {
fprintf(stderr," %s%x[%d%s%d] ", (f->fedges == fee) ? "*" : "",
PRETTY(fee),
fee->edge->v[0]->index,
fee->sign ? "<-" : "->",
fee->edge->v[1]->index);
if(fee->face != f)
fprintf(stderr," Fe %x: face %d (%x) != %x\n",
PRETTY(fee), fee->face - Fs, fee->face, f);
if(fee->next->prev != fee)
fprintf(stderr," Fe %x: next %x next->prev %x\n",
PRETTY(fee), PRETTY(fee->next), PRETTY(fee->next->prev));
e = fee->edge;
fee = fee->next;
} while(fee != fe);
fprintf(stderr, "\n");
}
E *
echeck(v0, v1)
register int v0, v1;
{
register E *e;
int t = (v0 + v1 + v0*v1) % nhash;
/* Symmetric hash function */
if(v0 > v1) v0 ^= v1, v1 ^= v0, v0 ^= v1;
for(e = ehash[t]; e != NULL; e = e->link) {
if(e->v[0] == &Vs[v0] && e->v[1] == &Vs[v1]) {
register Fe *fe;
fprintf(stderr, "E 0x%x %d-%d (%d-%d) %x...\n",
e, v0,v1, e->v[0]->index, e->v[1]->index, PRETTY(e->feds));
for(fe = e->feds; fe != NULL; fe = fe->elink) {
fecheck(fe);
}
}
}
return e;
}
normalize(f)
F *f;
{
register Fe *fe;
fe = f->fedges;
if(fe == NULL)
return;
if(fe->prev == fe->next) {
debug && printf("# Face %d already degenerate -- tossing it.\n", f - Fs);
deface(f);
return;
}
do { /* loop over edges on this face */
P n;
float r;
for(;;) {
register Fe *fn, *fee;
register P *pp, *qp;
pp = &fe->edge->to;
fee = fe->next;
qp = &fee->edge->to;
VCROSS(pp, qp, &n);
r = VDOT(&n, &n);
if(r > EPS_LINEAR)
break;
/*
* Join collinear edges; produce a new edge
*/
fn = fedge(f, fe->edge->v[fe->sign], fee->edge->v[1-fee->sign]);
fee->next->prev = fe->prev->next = fn;
fn->next = fee->next;
fn->prev = fe->prev;
if(fe == f->fedges || fee == f->fedges)
f->fedges = fn; /* preserve headiness */
unfedge(fee);
if(fee != fe)
unfedge(fe);
if(fn->prev == fn->next) {
/*
* This face became degenerate -- toss it.
*/
debug && printf("# degenerate face %d\n", f - Fs);
deface(f);
return;
}
fe = fn;
}
r = 1/sqrt(r);
if(n.x < 0 || (n.x == 0 && n.y < 0 || (n.y == 0 && n.z < 0)))
r = -r; /* Canonicalize */
fe->n.x = n.x*r; fe->n.y = n.y*r; fe->n.z = n.z*r;
} while((fe = fe->next) != f->fedges);
}
/*
* Delete a face, erasing all edges.
*/
deface(f)
F *f;
{
register Fe *fe, *fee;
fe = f->fedges;
if(fe == NULL)
return;
do {
fee = fe->next;
unfedge(fe);
fe = fee;
} while(fe != f->fedges);
f->fedges = NULL;
tossedf++;
}
vcmp(p, q)
V **p, **q;
{
register V *vp, *vq;
register float d;
vp = *p;
vq = *q;
d = vp->p.x - vq->p.x;
if(d < 0) return -1;
if(d > 0) return 1;
d = vp->p.y - vq->p.y;
if(d < 0) return -1;
if(d > 0) return 1;
d = vp->p.z - vq->p.z;
if(d < 0) return -1;
if(d > 0) return 1;
if(vp->p.w == vq->p.w) return 0;
return(vp->p.w < vq->p.w ? -1 : 1);
}
vmerge()
{
V **vp;
int i, j;
register V *a, *b;
int nexti;
vp = NewN(V *, nv);
for(i = 0, a = Vs; i < nv; i++) {
a->ref = 0;
vp[i] = a++;
}
qsort(vp, nv, sizeof(V *), vcmp);
/*
* Now all matches will occur within X-runs
*/
for(i = 0; i < nv; i = nexti ? nexti : i+1) {
nexti = 0;
a = vp[i];
if(a->ref)
continue;
for(j = i; ++j < nv; ) {
b = vp[j];
if(b->ref)
continue;
if(b->p.x - a->p.x > EPS_POINT)
break;
if(fabs(a->p.y - b->p.y) < EPS_POINT
&& fabs(a->p.z - b->p.z) < EPS_POINT
&& (vdim == 3 || fabs(a->p.w - b->p.w) < EPS_POINT)) {
debug && printf("# Vtx %d->%d\n", b->index, a->index);
b->index = a->index;
b->ref++;
} else if(!nexti)
nexti = j;
}
}
free(vp);
}